home *** CD-ROM | disk | FTP | other *** search
- /*
- C* -- Code generation -- machine independent part.
-
- source: g1.c
- started: November 9, 1985
- version:
- March 6, 1987
- February 22, 1989:
- all out_* routines moved to out.c
- out_nl, out_lit replaced by sysnlput, syssput
-
- PUBLIC DOMAIN SOFTWARE
-
- The CSTAR program was placed in the public domain on June 15, 1991,
- by its author and sole owner,
-
- Edward K. Ream
- 1617 Monroe Street
- Madison, WI 53711
- (608) 257-0802
-
- CSTAR may be used for any commercial or non-commercial purpose.
-
- See cstar.h or cstar.c for a DISCLAIMER OF WARRANTIES.
- */
- #include "cstar.h"
-
- /*
- Externally visible functions
- */
- void gen_init(void);
- void gen_function(struct fbody *p);
-
- /*
- Internal functions
- */
- static void gen_do (struct node *p);
- static void gen_if (struct node *p);
- static void gen_for (struct node *p);
- static void gen_switch (struct node *p);
- static void gen_while (struct node *p);
- static void gen_break (struct node *p);
- static void gen_case (struct node *p);
- static void gen_continue (struct node *p);
- static void gen_default (struct node *p);
- static void gen_return (struct node *p);
- static void gen_goto (struct node *p);
- static void gen_label (struct node *p);
- static void gen_ldef (struct node *p);
- static void gen_x (struct node *p);
- static void gen_z (struct node *p);
- static bool is_vbool (struct node *p);
-
- /*
- Shared register data
- */
- extern int sa_used [8];
- extern int sd_used [8];
- extern int sa_push[8];
- extern int sd_push[8];
-
- #define g_bra(p) g_1lab(X_BRA, p)
-
- /*
- Data structures used only by this module.
- */
- static struct node * brk_lab;
- static struct node * cont_lab;
- static struct node * ret_lab;
- static int ret_a, ret_d;
-
- /*
- Initialize this module.
- */
- void
- gen_init(void)
- {
- TICK("gen_init");
-
- brk_lab = NULL;
- cont_lab = NULL;
- ret_lab = NULL;
-
- code_head = NULL;
- code_tail = NULL;
-
- g_init();
-
- int_type = new_tnode();
- byte_type = new_tnode();
- byte_type -> t_tsize = 1;
- byte_type -> t_mclass |= CHAR_MOD;
- long_type = new_tnode();
- long_type -> t_tsize = LONG_SIZE;
- long_type -> t_mclass |= LONG_MOD;
-
- segment = -1;
- }
-
- /*
- Generate code for a list of local definitions/declarations.
-
- WARNING: not ready yet.
- */
- void
- gen_ldef(register struct node *p)
- {
- TICK("gen_ldef");
- }
-
- /*
- Append code generated for a list of statements to the global code list.
- */
- static void
- gen_list(register struct node * p)
- {
- TRACEPB("gen_list", printf("(%p)\n", p));
-
- for (; p; p = p -> n_next) {
- g_line(p -> n_linno);
-
- TRACEP("gen_list",
- printf("token %d %s\n", p -> n_type,
- ps_tok(p -> n_type)));
-
- switch (p -> n_type) {
- case K_IF: gen_if(p); break;
- case K_DO: gen_do(p); break;
- case K_WHILE: gen_while(p); break;
- case K_FOR: gen_for(p); break;
- case K_SWITCH: gen_switch(p); break;
- case K_CASE: gen_case(p); break;
- case K_RETURN: gen_return(p); break;
- case K_GOTO: gen_goto(p); break;
- case K_BREAK: gen_break(p); break;
- case K_CONTINUE: gen_continue(p); break;
- case K_DEFAULT: gen_default(p); break;
- case Z_TOK: gen_z(p); break;
- case X_TOK: gen_x(p); break;
- case LABEL_TOK: gen_label(p); break;
-
- default:
- /* must be an expr, if not, gen_expr will catch it */
- TRACEP("gen_list", printf("expression %p->%p\n",
- p, p -> n_cltype));
- gen_expr(p);
- }
- }
-
- TICKX("gen_list");
- }
-
- /*
- Generate code for a function.
-
- 1. Generate the entry code, body, and exit code.
- 2. Do optimization
- 3. Output the code
-
- The caller does gen_init.
-
- CAUTION: the long link frame size is of an unsigned type; it is not
- signed long. It causes the stack to be offset by an unsigned
- negative amount, although it is not itself treated as unsigned
- negative. See note in reg.c/decl_alloc().
- */
- void
- gen_function(register struct fbody * p)
- {
- register unsigned long link_size;
- register int i;
- int r_push, do_addq;
- static unsigned long t_err = 0;
-
- TRACEPB("gen_function",
- printf("%p: %s; call_1arg %d\n", p, p -> fname, call_1arg));
-
- if (tree_flag) {
- sysnlput();
- syssput(">>>>> Parse Tree for ");
- syssput(p -> fname);
- syssput("()");
- sysnlput();
- out_tree(p -> parse);
- sysnlput();
- }
-
- if (nogen_flag) {
- /* Suppress code generation. */
- RETURN_VOID("gen_function");
- }
-
- if (t_errcount) {
- if (t_err == 0) {
- /* do something to guarantee an assembler error */
- /* this way it shows up on the "comp" printout */
- sysnlput();
- syssput("&& error");
- sysnlput();
- }
- syssput("* ");
- syssput(p -> fname);
- sysnlput();
- t_err = t_errcount;
- RETURN_VOID("gen_function");
- }
-
- /* if call_1arg optimization is unwanted, set call_1arg=0 here */
-
- link_size = p -> lcl_size;
-
- /* decide about return register */
- TRACEP("gen_function",
- printf("function returns type: ");
- pr_type(p -> ftype -> t_link);
- printf("\n");
- );
-
- i = p -> ftype -> t_link -> t_typtok;
- if (i == INT_TYPE) {
- ret_a = FALSE;
- ret_d = TRUE;
- }
- else if (i != VOID_TYPE) {
- #ifdef D0_ONLY
- ret_a = FALSE;
- ret_d = TRUE;
- #else
- ret_a = TRUE;
- ret_d = FALSE;
- #endif /* D0_ONLY */
- }
- else {
- ret_a = ret_d = FALSE;
- }
- TRACEP("gen_function",printf("ret_d = %d ret_a = %d\n", ret_d,ret_a));
-
- /* Set the global return label for this function. */
- ret_lab = new_clabel();
-
- /* Generate the body. */
- gen_list(p -> parse);
-
- /* Generate the exit */
- g_label(ret_lab);
-
-
- /* ------------ register allocation ------------ */
-
- /* identify the registers which need to be pushed */
- r_push = FALSE;
- for (i = 0; i <= 7; i++) {
- sa_push[i] = sa_used[i];
- sd_push[i] = sd_used[i];
- }
-
- TRACEP("gen_function",
- printf("used: d:");
- for (i = 0; i <= 7; i++) {
- printf(" %d", sd_push[i]);
- }
- printf(" a:");
- for (i = 0; i <= 7; i++) {
- printf(" %d", sa_push[i]);
- }
- printf("\n");
- );
-
- /* do not push those registers treated as SCRATCH, no matter how
- they got used (drawn as temps or named explicitly;)
- the caller is responsible for them */
- for (i = 0; i <= ASCRATCH; i++) {
- sa_push[i] = FALSE;
- }
- for (i = 0; i <= DSCRATCH; i++) {
- sd_push[i] = FALSE;
- }
-
- /* do not push the return register, since popping it would
- clobber the return */
- if (ret_a) {
- sa_push[0] = FALSE;
- }
- else if (ret_d) {
- sd_push[0] = FALSE;
- }
-
- TRACEP("gen_function",
- printf("push: d:");
- for (i = 0; i <= 7; i++) {
- printf(" %d", sd_push[i]);
- }
- printf(" a:");
- for (i = 0; i <= 7; i++) {
- printf(" %d", sa_push[i]);
- }
- printf("\n");
- );
-
- for (i = 0; i <= 7; i++) {
- if (sa_push[i] || sd_push[i]) {
- r_push = TRUE;
- break;
- }
- }
- TRACEP("gen_function", printf("r_push = %d\n", r_push));
-
- /*
- insert an extra register to push, or flag an extra addq.
- this obviates the need for calls to functions with one
- argument to adjust the stack. it saves a significant amount
- of code, several percent.
- */
- do_addq = FALSE;
-
- /* NOTE: for speed, >= 2 may be better if there are few calls */
- if (call_1arg >= 1) {
- if (r_push) {
- if (ret_d) {
- if (sd_push[1]) {
- /* no possible extra push */
- do_addq = TRUE;
- /* WARNING */
- t_error("gen_function: case not done yet");
- }
- else {
- sd_push[1] = TRUE;
- }
- }
- else {
- /* d0 is the extra push */
- sd_push[0] = TRUE;
- }
- }
- else {
- link_size += 4;
- }
- }
- out_function(p, link_size, r_push, do_addq);
-
- TICKX("gen_function");
- }
-
- /*
- Generate code for the do statement.
- */
- static void
- gen_do(register struct node * p)
- {
- struct node *loop1_lab, *loop2_lab;
- struct node *old_cont, *old_brk;
-
- /* Save old values. */
-
- TRACEPB("gen_do", printf("(%p)\n", p));
-
- old_brk = brk_lab;
- old_cont = cont_lab;
-
- /* Get new values. */
- loop1_lab = new_clabel();
- loop2_lab = new_clabel();
- cont_lab = new_clabel();
- brk_lab = new_clabel();
-
- gen_pp(p -> n_dbool);
- if (is_vbool(p -> n_dbool)) {
- g_bra(loop2_lab);
- g_label(loop1_lab);
- gen_bpost(p -> n_dbool);
- g_label(loop2_lab);
- gen_list(p -> n_dbdy);
- g_label(cont_lab);
- gen_bool(p -> n_dbool, loop1_lab, FALL_THROUGH);
- gen_bpost(p -> n_dbool);
- g_label(brk_lab);
- }
- else {
- g_label(cont_lab);
- gen_list(p -> n_dbdy);
- g_bra(cont_lab);
- g_label(brk_lab);
- }
-
- /* Restore old values. */
- brk_lab = old_brk;
- cont_lab = old_cont;
-
- TICKX("gen_do");
- }
-
- /*
- Generate code for an "if" statement.
-
- In general, we need to generate an else clause even if none was
- given explicitly so that there will be a place to put the sequence
- points for each branch of the boolean expression.
- */
- static void
- gen_if(register struct node * p)
- {
- struct node *else_lab, *end_lab;
-
- TRACEPB("gen_if", printf("(%p)\n", p));
-
- else_lab = new_clabel();
- gen_pp(p -> n_ibool);
-
- gen_bool(p -> n_ibool, FALL_THROUGH, else_lab);
- gen_bpost(p -> n_ibool);
- gen_list(p -> n_ithen);
-
- /* cut down on generation of spurious nodes since a missing
- else clause is quite common */
- if (p -> n_ielse) {
- g_bra(end_lab = new_clabel());
- g_label(else_lab);
- gen_bpost(p -> n_ibool);
- gen_list(p -> n_ielse);
- g_label(end_lab);
- }
- else {
- g_label(else_lab);
- }
-
- TICKX("gen_if");
- }
-
- /*
- Generate code for the for statement.
- */
- static void
- gen_for(register struct node * p)
- {
- struct node *lab0, *lab1;
- struct node *old_brk, *old_cont;
-
- /* Save old values. */
-
- TRACEPB("gen_for", printf("(%p)\n", p));
-
- old_brk = brk_lab;
- old_cont = cont_lab;
-
- lab0 = new_clabel();
- lab1 = new_clabel();
- brk_lab = new_clabel();
- cont_lab = new_clabel();
-
- gen_pp(p -> n_fbool);
- if (is_vbool(p -> n_fbool)) {
- gen_expr(p -> n_f1list);
- g_bra(lab0);
- g_label(lab1);
- gen_bpost(p -> n_fbool);
- gen_list(p -> n_fbdy);
- g_label(cont_lab);
- gen_expr(p -> n_f2list);
- g_label(lab0);
- gen_bool(p -> n_fbool, lab1, FALL_THROUGH);
- gen_bpost(p -> n_fbool);
- g_label(brk_lab);
- }
- else {
- gen_expr(p -> n_f1list);
- g_label(lab1);
- gen_list(p -> n_fbdy);
- g_label(cont_lab);
- gen_expr(p -> n_f2list);
- g_bra(lab1);
- g_label(brk_lab);
- }
-
- /* Restore old values. */
- brk_lab = old_brk;
- cont_lab = old_cont;
-
- TICKX("gen_for");
- }
-
- /*
- Generate code for the switch statement.
-
- Note: this may end up handling long cases if no fundamental
- problems turn up
- */
- static void
- gen_switch(register struct node * p)
- {
- struct node * old_brk;
- register struct node *q;
- register struct node *loc1;
-
- /* Save old value. */
-
- TRACEPB("gen_switch", printf("(%p)\n", p));
-
- old_brk = brk_lab;
-
- /* Generate break label. */
- brk_lab = new_clabel();
-
- /* Generate labels for all case statements. */
- for (q = p -> n_slist; q; q = q -> n_clist) {
- q -> n_clab = new_clabel();
- }
-
- /* Generate label for the default case. */
- q = p -> n_sdef;
- if (q) {
- q -> n_clab = new_clabel();
- }
-
- /* Generate code to evaluate the switch expression. */
- loc1 = gen_dexpr(p -> n_sval); /* not necessarily a dtemp! */
-
- /*
- There are a number of possible ways of going to the cases:
-
- 1. The obvious chain of compares and branches.
-
- 2. A binary tree of compares and branches.
-
- 3. A well-balanced binary tree of compares and branches.
-
- 4. An index-table jump.
-
- 5. A serial-search-table jump.
-
- 6. A binary-search-table jump.
- */
-
- /* Generate using method (1). */
- for (q = p -> n_slist; q; q = q -> n_clist) {
- g_2l2(X_CMP, locn_xconst(q -> n_ccon), loc1);
- g_1lab(X_BEQ, q -> n_clab);
- }
- q = p -> n_sdef;
- if (q) {
- g_bra(q -> n_clab);
- }
- else {
- g_bra(brk_lab);
- }
-
- /* Generate the body. */
- gen_list(p -> n_sbdy);
- g_label(brk_lab);
-
- /* Restore old break label. */
- brk_lab = old_brk;
-
- TICKX("gen_switch");
- }
-
- /*
- Generate code for the while statement.
- */
- static void
- gen_while(register struct node * p)
- {
- struct node *loop_lab;
- struct node *old_cont, *old_brk;
-
- /* Save old values. */
-
- TRACEPB("gen_while", printf("(%p)\n", p));
-
- old_brk = brk_lab;
- old_cont = cont_lab;
-
- /* Generate new labels. */
- loop_lab = new_clabel();
- cont_lab = new_clabel();
- brk_lab = new_clabel();
-
- gen_pp(p -> n_wbool);
- if (is_vbool(p -> n_wbool)) {
- g_bra(cont_lab);
- gen_bpost(p -> n_wbool);
- g_label(loop_lab);
- gen_list(p -> n_wbdy);
- g_label(cont_lab);
- gen_bool(p -> n_wbool, loop_lab, FALL_THROUGH);
- gen_bpost(p -> n_wbool);
- g_label(brk_lab);
- }
- else {
- g_label(cont_lab);
- gen_list(p -> n_wbdy);
- g_bra(cont_lab);
- }
-
- /* Restore old values. */
- brk_lab = old_brk;
- cont_lab = old_cont;
-
- TICKX("gen_while");
- }
-
- /*
- Generate code for the break statement.
- */
- static void
- gen_break(struct node *p)
- {
- TRACEPB("gen_break", printf("(%p)\n", p));
-
- g_bra(brk_lab);
-
- TICKX("gen_break");
- }
-
- /*
- Generate code for the case statement.
- */
- static void
- gen_case(register struct node * p)
- {
- TRACEPB("gen_case", printf("(%p)\n", p));
-
- g_label(p -> n_clab);
-
- TICKX("gen_case");
- }
-
- /*
- Generate code for the continue statement.
- */
- static void
- gen_continue(struct node *p)
- {
- TRACEPB("gen_continue", printf("(%p)\n", p));
-
- g_bra(cont_lab);
-
- TICKX("gen_continue");
- }
-
- /*
- Generate code for the default statement.
- */
- static void
- gen_default(struct node *p)
- {
- TRACEPB("gen_default", printf("(%p)\n", p));
-
- g_label(p -> n_clab);
-
- TICKX("gen_default");
- }
-
- /*
- Generate code for the return statement.
- */
- static void
- gen_return(register struct node * p)
- {
- TRACEPB("gen_return", printf("(%p)\n", p));
-
- if (p -> n_rval) {
- if (ret_a) {
- gen_a0exp(p -> n_rval);
- }
- else if (ret_d) {
- gen_d0exp(p -> n_rval);
- }
- }
-
- /* Jump to the return label. */
- g_bra(ret_lab);
-
- TICKX("gen_return");
- }
-
- /*
- Generate code for a goto to a user defined label.
- */
- static void
- gen_goto(struct node * p)
- {
- TRACEPB("gen_goto", printf("(%p)\n", p));
-
- g_bra(p -> n_plab);
-
- TICKX("gen_goto");
- }
-
- /*
- Generate a user defined label from a parse label node.
- */
- static void
- gen_label(struct node *p)
- {
- TRACEPB("gen_label", printf("(%p)\n", p));
-
- g_label(p -> n_plab);
-
- TICKX("gen_label");
- }
-
- /*
- Generate a machine-language mnemonic.
-
- NOTE: x_exists() gives the error messages now. This way it can be
- more specific and give the line numbers as well.
- */
- static void
- gen_x(register struct node *p)
- {
- register struct x_ex *x;
- register int type;
-
- TRACEPB("gen_x", printf("(%p)\n", p));
-
- type = p -> n_xtype;
-
- switch(type) {
-
- case X_JMP:
- case X_JSR:
-
- if (p -> n_xarg1) {
- /* these arguments will have to be generated somehow */
- /* the representation isn't clear right now */
- if (x_exists(p)) {
- g_1(type, p -> n_xarg1);
- }
- }
- else {
- g_1lab(type, p -> n_arg2);
- }
- break;
-
- case X_DBCC: case X_DBCS: case X_DBEQ:
- case X_DBF : case X_DBGE: case X_DBGT:
- case X_DBHI: case X_DBLE: case X_DBLS:
- case X_DBLT: case X_DBMI: case X_DBNE:
- case X_DBPL: case X_DBT: case X_DBVC:
- case X_DBVS:
-
- if (x_exists(p)) {
- g_2lab(type, p -> n_xarg1, p -> n_arg2);
- }
- break;
-
- case X_BRA: case X_BSR:
- case X_BCC: case X_BCS: case X_BEQ:
- case X_BGE: case X_BGT: case X_BHI:
- case X_BLE: case X_BLS: case X_BLT:
- case X_BMI: case X_BNE: case X_BPL:
- case X_BVC: case X_BVS:
-
- if (x_exists(p)) {
- g_1lab(type, p -> n_arg1);
- }
- break;
-
- default:
- /* verify that arguments are loc_node's */
- /* in C, arguments will be addresses of something */
- /* that something will be set up in assembler */
- gen_pp(p -> n_xarg1);
- gen_pp(p -> n_xarg2);
-
- /* See if the specified address modes exist for the function. */
- if (x = x_exists(p)) {
- g_x(p -> n_xtype, p -> n_xarg1,
- p -> n_xarg2, x -> xlen);
- }
- break;
- }
- RETURN_VOID("gen_x");
- }
-
- /*
- Generate a machine-language pseudo operation.
- */
- static void
- gen_z(register struct node *p)
- {
- TRACEPB("gen_z", printf("(%p)\n", p));
-
- /* CAUTION: see note in io.c, am_match(), about equates */
-
- switch (p -> n_ztype) {
-
- case Z_BSS: g_lit("BSS"); RETURN_VOID("gen_z");
- case Z_DATA: g_lit("DATA"); RETURN_VOID("gen_z");
- case Z_TEXT: g_lit("TEXT"); RETURN_VOID("gen_z");
-
- case Z_ORG: case Z_PUSH: case Z_POP:
- case Z_ADDSP: case Z_ADJSP: case Z_SUBSP:
-
- p -> n_zarg1 = pe_expr();
- RETURN_VOID("gen_z");
-
- case Z_DC: case Z_DCB: case Z_DCW: case Z_DCL:
- case Z_DS: case Z_DSB: case Z_DSW: case Z_DSL:
-
- p -> n_zarg1 = pe_list(CONST_EXPR);
- RETURN_VOID("gen_z");
- default:
- fatal("gen_z: unknown type");
- }
-
- TICKX("gen_z");
- }
-
- /*
- Return true if the parse tree that p points to is a variable boolean
- expression, i.e., it must be evaluated at run-time to see whether it
- is true or false.
- */
- bool
- is_vbool(register struct node * p)
- {
- /*
- NOT READY YET: see if root is a constant loc_node.
- */
- TRACEPB("is_vbool", printf("(%p)\n", p));
-
- if (p == NULL) {
- RETURN_BOOL("is_vbool", FALSE);
- }
- else {
- RETURN_BOOL("is_vbool", TRUE);
- }
- }
-